#include "Main.h"

// My header
#include "FileTools.h"

// Fridge
#include "StringHash.h"
#include "SuperTree.h"

// External
#include <gtk/gtk.h>
#include <errno.h>
#include <sys/types.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <ftw.h>

static int RemoveFile( const char* szPath, const struct stat *pxStatBuffer, int iFlag, struct FTW *pxFtwBuffer );
static int WalkDirectoryCallback( const char* szPath, const struct stat *pxStatBuffer, int iFlag, struct FTW *pxFtwBuffer );
static void FindInternal( SuperTree<FFileInfo>* pxFileList, const char* szPath, const char* szExtension, const bool bRecursive, const FHandle xParent );
static const char* GetExtensionForType( FRIDGE_FILE_TYPES eType );
static const char* GetDirectoryForType( FRIDGE_FILE_TYPES eType );
static const char* GetFilterForType( FRIDGE_FILE_TYPES eType );

static const char* szPLATFORM_TEMP_DIRECTORY = "/tmp";

void FileTools::Platform_FindFiles ( SuperTree<FFileInfo>* pxFileList, FRIDGE_FILE_TYPES eType, const bool bRecursive )
{	
	char szDir[ PATH_MAX ];
	szDir[0] = 0;
	
	strcpy( szDir, GetDirectoryForType( eType ) );
	strcat( szDir, "/" );
	
	FindInternal( pxFileList, szDir, GetExtensionForType( eType ), bRecursive, TREE_HANDLE_HEAD );
}

bool FileTools::Platform_SavePrompt( FRIDGE_FILE_TYPES eType, char* szSaveFile )
{
	gtk_init_check(0, NULL);
	
	GtkWidget* dialog;
	dialog = gtk_file_chooser_dialog_new ("Save File",
	                                      NULL,
	                                      GTK_FILE_CHOOSER_ACTION_SAVE,
	                                      GTK_STOCK_CANCEL,
	                                      GTK_RESPONSE_CANCEL,
	                                      GTK_STOCK_SAVE,
	                                      GTK_RESPONSE_ACCEPT,
	                                      NULL);
	if(!dialog)
	{
		return false;
	}
	
	gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
	
	gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), GetDirectoryForType(eType));
	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "NewPreset");
	
	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
	{
		strcpy(szSaveFile, gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)));
	}
	gtk_widget_destroy (dialog);
	
	while(gtk_events_pending())
	{
		gtk_main_iteration();
	}
	
	return true;
}

bool FileTools::Platform_OpenPrompt( FRIDGE_FILE_TYPES eType, char* szOpenFile )
{
	gtk_init_check(0, NULL);
	
	GtkWidget *dialog;
	dialog = gtk_file_chooser_dialog_new ("Open File",
	                                      NULL,
	                                      GTK_FILE_CHOOSER_ACTION_OPEN,
	                                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	                                      GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
	                                      NULL);
	if(!dialog)
	{
		return false;
	}
	
	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
	{
		strcpy(szOpenFile,gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)));
	}
	gtk_widget_destroy (dialog);
	
	while(gtk_events_pending())
	{
		gtk_main_iteration();
	}
	
	return true;
}


bool FileTools::Platform_MakeDirectory( const char* szDirectoryPath )
{
	if( mkdir( szDirectoryPath, 0777 ) == -1 )
	{
		FridgeLogf( "mkdir error: %s", strerror( errno ) );
		return false;
	}
	
	return true;
}

bool FileTools::Platform_RenameDirectory( const char* szOldDirectory, const char* szNewDirectory )
{
	if( rename( szOldDirectory, szNewDirectory ) == -1 )
	{
		FridgeLogf( "rename error: %s", strerror( errno ) );
		return false;
	}
	
	return true;
}

bool FileTools::Platform_RemoveTempDirectory()
{
	char szTempPath[ PATH_MAX ];
	sprintf( szTempPath, "%s/fridge", szPLATFORM_TEMP_DIRECTORY );
	
	// Report files before directories
	int iFlags = 0;
	iFlags |= FTW_DEPTH;
	
	// Empty fridge temp dir
	if( nftw( szTempPath, RemoveFile, 200, iFlags ) == -1 )
	{
		FridgeLogf( "RemoveTempDir error: %s", strerror( errno ) );
		return false;
	}
	
	return true;
}

void FileTools::Platform_WalkDirectory( const char* szDirectoryPath, DirectoryWalkCallback pfnCallback )
{
	// Report files before directories
	int iFlags = 0;
	iFlags |= FTW_DEPTH;
	
	// Empty fridge temp dir
	nftw( szDirectoryPath, WalkDirectoryCallback, 200, iFlags );
}

int WalkDirectoryCallback( const char* szPath, const struct stat *pxStatBuffer, int iFlag, struct FTW *pxFtwBuffer )
{
	// We just want files, not directories
	if( iFlag == FTW_F )
	{
		FileTools::AddToArchive( szPath, "/tmp//fridge/" );
	}
	return 0;
}

int RemoveFile( const char* szPath, const struct stat *pxStatBuffer, int iFlag, struct FTW *pxFtwBuffer )
{
	// DON'T CALL THIS FUNCTION MANUALLY. EVER.
	
	remove( szPath );
	return 0;
}

const char* FileTools::Platform_GetTempDirectory()
{
	return szPLATFORM_TEMP_DIRECTORY;
}


// ___________________________________________________________________________________________

void FindInternal( SuperTree<FFileInfo>* pxFileList, const char* szPath, const char* szExtension, const bool bRecursive, const FHandle xParent )
{
	if( !pxFileList )
	{
		return;
	}
	
	DIR* d_fh;
	struct dirent* entry;
	char szFullString[ PATH_MAX ];
	strcpy( szFullString, szPath );
	strcat( szFullString, "*.*" );
	
	while( (d_fh = opendir(szPath)) == NULL )
	{
		fprintf(stderr, "Could not open directory: %s\n", szPath);
		exit(-1);
	}
	
	while( (entry = readdir(d_fh)) != NULL )
	{
		if (strncmp(entry->d_name, "..", 2) != 0 &&
			strncmp(entry->d_name, ".",  1) != 0)
		{
			if (entry->d_type == DT_DIR) 
			{
				if (bRecursive) 
				{
					char szSubPath[ PATH_MAX ];
					strcpy( szSubPath, szPath );
					sprintf( szSubPath, "%s%s/", szPath, entry->d_name );
					
					const FHandle xNewDirectoryHandle = pxFileList->Add( new FFileInfo( szSubPath, true ) );
					FindInternal( pxFileList, szSubPath, szExtension, true, xNewDirectoryHandle );
				}
			}
			else
			{
				const u_int uExtensionLength = strlen( szExtension );
				const u_int uNameLength = strlen( entry->d_name );
				const bool  bHasExtension = !strcmp( &( entry->d_name[ uNameLength - uExtensionLength ] ), szExtension );
				
				if (bHasExtension)
				{
					char szFull[ PATH_MAX ];
					sprintf( szFull, "%s%s", szPath, entry->d_name );
					FFileInfo xInfo;
					
					printf("%s\n", szFull);

					pxFileList->Add( new FFileInfo( szFull, false ), xParent );
				}
			}
		}		
	}
	
	// We're done here
	closedir(d_fh);
}

const char* GetFilterForType( FRIDGE_FILE_TYPES eType )
{
	
	static const char* aszFilter[] = {
		"Fridge Presets\0*.fridge\0",      // FRIDGE_FILE_PRESET
		"Fridge Templates\0*.template\0",  // FRIDGE_FILE_TEMPLATE
	};
	
	return aszFilter[ eType ];
	
}

const char* GetExtensionForType( FRIDGE_FILE_TYPES eType )
{
	static const char* aszExtensions[] = {
		"fridge",    // FRIDGE_FILE_PRESET
		"template",  // FRIDGE_FILE_TEMPLATE
	};
	
	return aszExtensions[ eType ];
}

const char* GetDirectoryForType( FRIDGE_FILE_TYPES eType )
{
	
	// TODO: At some point we need to prepend the User directory to this rather than using the directory where fridge is installed
	
	static const char* aszDirectory[] =  {
		"Presets",    // FRIDGE_FILE_PRESET
		"Templates",  // FRIDGE_FILE_TEMPLATE
	};
	
	return aszDirectory[ eType ];
}

//eof
